#include "Precompile.h"
#include "CEGUIManager.h"

#include <elements/CEGUIListBox.h>
#include <elements/CEGUIPushButton.h>

#ifdef TARGET_DX11
#include "DX11RenderSystem.h"
#elif defined (TARGET_DX10)
#include "DX10RenderSystem.h"
#elif defined (TARGET_DX9)
#include "DX9RenderSystem.h"
#endif

namespace Graphics{
	CEGUIManager::CEGUIManager(){
		m_inputMutex = ThreadLib::ThreadFactory::spawnMutex();
		m_listBoxesMutex = ThreadLib::ThreadFactory::spawnMutex();
		m_buttonMutex = ThreadLib::ThreadFactory::spawnMutex();
	}

	CEGUIManager::~CEGUIManager(){
		CEGUIRenderer::destroySystem();
	}

	void CEGUIManager::initializeManager(RenderContextPtr context){
		try{
#ifdef TARGET_DX11
		DX11RenderContextPtr ctx = boost::static_pointer_cast<DX11RenderContext>(context);
		m_renderer = &CEGUIRenderer::bootstrapSystem(ctx->m_handle,ctx->m_context);
#elif defined (TARGET_DX10)
		DX10RenderContextPtr ctx = boost::static_pointer_cast<DX10RenderContext>(context);
		m_renderer = &CEGUIRenderer::bootstrapSystem(ctx->m_handle,ctx->m_context);
#elif defined (TARGET_DX9)
		DX9RenderContextPtr ctx = boost::static_pointer_cast<DX9RenderContext>(context);
		m_renderer = &CEGUIRenderer::bootstrapSystem(ctx->m_handle);
#endif
		}
		catch(CEGUI::RendererException e){
			std::string test = e.what();
			LOG_ERROR(test);
		}

		// Set up resource directories
		CEGUI::DefaultResourceProvider* rp = static_cast<CEGUI::DefaultResourceProvider*>(CEGUI::System::getSingleton().getResourceProvider());

		std::string basePath = Core::GeneralSettings::getDataPath();
		rp->setResourceGroupDirectory("imagesets", std::string(basePath+"GUIFiles/imagesets/"));
		rp->setResourceGroupDirectory("animations", std::string(basePath+"GUIFiles/animations/"));
		rp->setResourceGroupDirectory("configs", std::string(basePath+"GUIFiles/configs/"));
		rp->setResourceGroupDirectory("fonts", std::string(basePath+"GUIFiles/fonts/"));
		rp->setResourceGroupDirectory("layouts", std::string(basePath+"GUIFiles/layouts/"));
		rp->setResourceGroupDirectory("looknfeel", std::string(basePath+"GUIFiles/looknfeel/"));
		rp->setResourceGroupDirectory("lua_scripts", std::string(basePath+"GUIFiles/lua_scripts/"));
		rp->setResourceGroupDirectory("schemes", std::string(basePath+"GUIFiles/schemes/"));
		rp->setResourceGroupDirectory("xml_schemas", std::string(basePath+"GUIFiles/xml_schemas/"));

		// set the default resource groups to be used
		CEGUI::Imageset::setDefaultResourceGroup("imagesets");
		CEGUI::Font::setDefaultResourceGroup("fonts");
		CEGUI::Scheme::setDefaultResourceGroup("schemes");
		CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeel");
		CEGUI::WindowManager::setDefaultResourceGroup("layouts");
		CEGUI::ScriptModule::setDefaultResourceGroup("lua_scripts");

		// setup default group for validation schemas
		CEGUI::XMLParser* parser = CEGUI::System::getSingleton().getXMLParser();
		if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))
			parser->setProperty("SchemaDefaultResourceGroup", "schemas");

		// create (load) the TaharezLook scheme file
		// (this auto-loads the TaharezLook looknfeel and imageset files)
		CEGUI::SchemeManager::getSingleton().create( "TaharezLook.scheme" );

		// create (load) a font.
		// The first font loaded automatically becomes the default font, but note
		// that the scheme might have already loaded a font, so there may already
		// be a default set - if we want the "Commonweath-10" font to definitely
		// be the default, we should set the default explicitly afterwards.
		CEGUI::FontManager::getSingleton().create( "DejaVuSans-10.font" );
		m_listBoxFont = &CEGUI::FontManager::getSingleton().create( "ListBoxItemText.font" );

		CEGUI::System::getSingleton().setDefaultFont( "DejaVuSans-10" );
		CEGUI::System::getSingleton().setDefaultMouseCursor( "TaharezLook", "MouseArrow" );
		CEGUI::System::getSingleton().setDefaultTooltip( "TaharezLook/Tooltip" );

		CEGUI::Window* myRoot = CEGUI::WindowManager::getSingleton().loadWindowLayout( "Editor.layout" );
		CEGUI::System::getSingleton().setGUISheet( myRoot );

		m_initialized = true;
	}

	void CEGUIManager::render(RenderContextPtr context){
		if(!m_initialized)
			initializeManager(context);

		updateListBoxes();
		updateButtonSubscriptions();
		injectInput(context->m_dt);
		CEGUI::System::getSingleton().injectTimePulse(context->m_dt);

		context->m_renderSystem->saveRenderStates();
		CEGUI::System::getSingleton().renderGUI();
		context->m_renderSystem->restoreRenderStates();
	}

	void CEGUIManager::injectKeyInput(Input::KeyDefinition key, bool down){
		m_inputMutex->lock();
		m_keyPresses.push_back(std::pair<Input::KeyDefinition,bool>(key,down));
		m_inputMutex->unlock();
	}

	void CEGUIManager::injectMouseInput(float x,float y){
		m_inputMutex->lock();
		m_mouseMovements.push_back(std::pair<float,float>(x,y));
		m_inputMutex->unlock();
	}

	void CEGUIManager::showCursor(bool var){
		if(CEGUI::MouseCursor::getSingletonPtr())
			CEGUI::MouseCursor::getSingletonPtr()->setVisible(var);
	}

	void CEGUIManager::showGUI(bool var){
		//CEGUI::WindowManager::getSingletonPtr()->
	}

	bool CEGUIManager::getVisible(std::string name){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return false;

		CEGUI::Window* win = winMgr->getWindow(name);
		return win->isVisible();
	}

	void CEGUIManager::setVisible(std::string name, bool var){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return;

		CEGUI::Window* win = winMgr->getWindow(name);
		win->setVisible(var);
	}

	void CEGUIManager::setText(std::string name, std::string text){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return;

		CEGUI::Window* window = winMgr->getWindow(name);
		window->setText(text);
	}

	// List functions
	void CEGUIManager::clearList(std::string listBox){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return;

		CEGUI::Window* window = winMgr->getWindow(listBox);
		CEGUI::Listbox* listBoxPtr = (CEGUI::Listbox*)window;
		for(unsigned int i = 0; i<listBoxPtr->getItemCount(); i++){
			removeListBoxItem(listBox,listBoxPtr->getListboxItemFromIndex(i)->getText().c_str());
		}
	}

	void CEGUIManager::addListBoxItem(std::string listBox, std::string itemName){
		m_listBoxesMutex->lock();
		m_listBoxes.push_back(std::pair<bool,std::pair<std::string,std::string>>(true,std::pair<std::string,std::string>(listBox,itemName)));
		m_listBoxesMutex->unlock();
	}

	void CEGUIManager::removeListBoxItem(std::string listBox, std::string itemName){
		m_listBoxesMutex->lock();
		m_listBoxes.push_back(std::pair<bool,std::pair<std::string,std::string>>(false,std::pair<std::string,std::string>(listBox,itemName)));
		m_listBoxesMutex->unlock();
	}

	std::string CEGUIManager::getSelectedItemFromList(std::string listName){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return "";

		std::string result = "";

		m_listBoxesMutex->lock();
		CEGUI::Window* window = winMgr->getWindow(listName);
		CEGUI::Listbox* listBoxPtr = (CEGUI::Listbox*)window;
		for(unsigned int i = 0; i<listBoxPtr->getItemCount(); i++){
			if(listBoxPtr->getListboxItemFromIndex(i)->isSelected()){
				result = listBoxPtr->getListboxItemFromIndex(i)->getText().c_str();
			}
		}
		m_listBoxesMutex->unlock();

		return result;
	}

	void CEGUIManager::subscribeToButtonClick(std::string buttonName, boost::function<void(void)> func){
		m_buttonMutex->lock();
		m_buttonFunctions.push_back(std::pair<std::string,boost::function<void(void)>>(buttonName,func));
		m_buttonMutex->unlock();
	}

	void CEGUIManager::updateListBoxes(){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return;

		m_listBoxesMutex->lock();
		for(unsigned int i=0; i<m_listBoxes.size(); i++){
			CEGUI::Window* window = winMgr->getWindow(m_listBoxes[i].second.first);
			CEGUI::Listbox* listBoxPtr = (CEGUI::Listbox*)window;
			
			if(m_listBoxes[i].first){
				CEGUI::ListboxTextItem* item = new CEGUI::ListboxTextItem(m_listBoxes[i].second.second);

				CEGUI::colour col = CEGUI::colour(0.5,0.5,0.5);
				item->setSelectionColours(col);
				item->setSelectionBrushImage("TaharezLook","ComboboxSelectionBrush");
				item->setFont(m_listBoxFont);
				listBoxPtr->addItem(item);
			}
			else{
				for(unsigned int j=0; j<listBoxPtr->getItemCount(); j++){
					if(listBoxPtr->getListboxItemFromIndex(j)->getText().compare(m_listBoxes[i].second.second) == 0){
						listBoxPtr->removeItem(listBoxPtr->getListboxItemFromIndex(j));
					}
				}
			}
		}
		m_listBoxes.clear();
		m_listBoxesMutex->unlock();
	}

	void CEGUIManager::updateButtonSubscriptions(){
		CEGUI::WindowManager* winMgr = CEGUI::WindowManager::getSingletonPtr();
		if(!winMgr)
			return;

		m_buttonMutex->lock();
		for(unsigned int i=0; i<m_buttonFunctions.size(); i++){
			CEGUI::Window* window = winMgr->getWindow(m_buttonFunctions[i].first);
			CEGUI::PushButton* buttonPtr = (CEGUI::PushButton*)window;
			
			CEGUI::SubscriberSlot slot = CEGUI::SubscriberSlot(&CEGUIManager::buttonClickCallback,this);
			m_eventConnections[m_buttonFunctions[i].first] = buttonPtr->subscribeEvent(CEGUI::PushButton::EventMouseButtonUp,slot);
			m_buttonMapping[m_buttonFunctions[i].first] = m_buttonFunctions[i].second;
		}
		m_buttonFunctions.clear();
		m_buttonMutex->unlock();
	}

	void CEGUIManager::injectInput(float dt){
		m_inputMutex->lock();
		if(m_injectInput){
			CEGUI::System& system = CEGUI::System::getSingleton();

			for(unsigned int i=0; i<m_mouseMovements.size(); i++){
				system.injectMouseMove(m_mouseMovements[i].first,m_mouseMovements[i].second);
			}
			for(unsigned int i=0; i<m_keyPresses.size(); i++){
				switch(m_keyPresses[i].first){
				case Input::EIMOUSEBUTTON_0:
					if(m_keyPresses[i].second)
						system.injectMouseButtonDown(CEGUI::LeftButton);
					else
						system.injectMouseButtonUp(CEGUI::LeftButton);
					break;
				}
			}
		}
		m_mouseMovements.clear();
		m_keyPresses.clear();
		
		m_inputMutex->unlock();
	}

	bool CEGUIManager::buttonClickCallback(const CEGUI::EventArgs& args){
		CEGUI::WindowEventArgs& winArgs = (CEGUI::WindowEventArgs&)args;
		std::string name = winArgs.window->getName().c_str();

		std::map<std::string,boost::function<void(void)>>::iterator itr;
		itr = m_buttonMapping.find(name);
		if(itr != m_buttonMapping.end()){
			itr->second();
			return true;
		}
		return false;
	}
}